package com.base.server.search.utils;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wltea.analyzer.lucene.IKAnalyzer;

import com.base.server.search.pojo.SearchModel;
import com.system.comm.utils.FrameFileUtil;

/**
 * 搜索工具类
 * @author yuejing
 * @date 2019年7月31日 上午9:42:21
 */
public class LuceneUtil {

	private static final Logger LOGGER = LoggerFactory.getLogger(LuceneUtil.class);

	/**
	 * 创建索引
	 * @param searchModel
	 * @param indexPath
	 */
	public static void createIndex(SearchModel searchModel, String indexPath) {
		File dir = new File(indexPath);
		if (!dir.exists()) {
			FrameFileUtil.createDir(dir);
		}
		//3 建立索引(lucene)
		Directory directory = null;
		try {
			directory = FSDirectory.open(Paths.get(indexPath));
		} catch (IOException e) {
			LOGGER.error(e.getMessage(), e);
		}
		//索引库目录
		//分词器
		Analyzer analyzer = new IKAnalyzer();
		IndexWriterConfig iwc = new IndexWriterConfig(analyzer);

		// >>3.1   将Article转为Document
		/* Store参数说明
			No 本字段的原始值不存储
			YES 本字段的原始值会存在出在数据库区中
			如果不存在，则搜索出来的结果中这个字段的值为null */

		/*
	     * 自Lucene4开始 创建field对象使用不同的类型 只需要指定是否需要保存源数据 不需指定分词类别  
	     * 之前版本的写法如下  
	     * doc.Add(new Field("id", article.id.ToString(), Field.Store.YES, Field.Index.ANALYZED)); 
	     */
		Document doc = new Document();
		doc.add(new TextField("id", searchModel.getId(), Store.YES));
		doc.add(new TextField("title", searchModel.getTitle(), Store.YES));
		doc.add(new TextField("content", searchModel.getContent(), Store.YES));

		try {
			// >>3.2 保存到索引库中
			IndexWriter indexWriter = new IndexWriter(directory, iwc);
			indexWriter.addDocument(doc);
			//释放资源
			indexWriter.close();
		} catch (IOException e) {
			LOGGER.error(e.getMessage(), e);
		}
	}
	/**
	 * 更新索引
	 * @param searchModel
	 * @param indexPath
	 */
	public static void updateIndex(SearchModel searchModel, String indexPath) {
		File dir = new File(indexPath);
		if (!dir.exists()) {
			FrameFileUtil.createDir(dir);
		}
		//3 建立索引(lucene)
		Directory directory = null;
		try {
			directory = FSDirectory.open(Paths.get(indexPath));
		} catch (IOException e) {
			LOGGER.error(e.getMessage(), e);
		}
		//索引库目录
		//分词器
		Analyzer analyzer = new IKAnalyzer();
		IndexWriterConfig iwc = new IndexWriterConfig(analyzer);

		// >>3.1   将Article转为Document
		/* Store参数说明
			No 本字段的原始值不存储
			YES 本字段的原始值会存在出在数据库区中
			如果不存在，则搜索出来的结果中这个字段的值为null */
		/*
	     * 自Lucene4开始 创建field对象使用不同的类型 只需要指定是否需要保存源数据 不需指定分词类别  
	     * 之前版本的写法如下  
	     * doc.Add(new Field("id", article.id.ToString(), Field.Store.YES, Field.Index.ANALYZED)); 
	     */
		Document doc = new Document();
		doc.add(new TextField("id", searchModel.getId(), Store.YES));
		doc.add(new TextField("title", searchModel.getTitle(), Store.YES));
		doc.add(new TextField("content", searchModel.getContent(), Store.YES));
		try {
			// >>3.2 保存到索引库中
			IndexWriter indexWriter = new IndexWriter(directory, iwc);
			Term term = new Term("id", searchModel.getId());
			// 不管更新与否，先删除原来的
			indexWriter.deleteDocuments(term);
			indexWriter.addDocument(doc);
			//释放资源
			indexWriter.close();
		} catch (IOException e) {
			LOGGER.error(e.getMessage(), e);
		}

	}
	/**
	 * 删除索引
	 * @param searchModel
	 * @param indexPath
	 */
	public static void deleteIndex(SearchModel searchModel, String indexPath) {
		File dir = new File(indexPath);
		if (!dir.exists()) {
			FrameFileUtil.createDir(dir);
		}
		//3 建立索引(lucene)
		Directory directory = null;
		try {
			directory = FSDirectory.open(Paths.get(indexPath));
		} catch (IOException e) {
			LOGGER.error(e.getMessage(), e);
		}
		//索引库目录
		//分词器
		Analyzer analyzer = new IKAnalyzer();
		IndexWriterConfig iwc = new IndexWriterConfig(analyzer);
		try {
			// >>3.2 保存到索引库中
			IndexWriter indexWriter = new IndexWriter(directory, iwc);
			Term term = new Term("id", searchModel.getId());
			// 不管更新与否，先删除原来的
			indexWriter.deleteDocuments(term);
			//释放资源
			indexWriter.close();
		} catch (IOException e) {
			LOGGER.error(e.getMessage(), e);
		}
	}

	/**
	 * 搜索
	 * @param searchStrings	搜索字符串数组，需要与searchFields的长度一致和一一对应
	 * @param searchFields	搜索的字段数组
	 * @param indexPath		索引的目录
	 */
	public static List<SearchModel> search(String[] searchStrings, String[] searchFields, String indexPath) {
		//2 执行搜索(lucene)
		List<SearchModel> models = new ArrayList<SearchModel>();
		try {
			//索引库目录
			Directory directory = FSDirectory.open(Paths.get(indexPath));
			//分词器
			Analyzer analyzer = new IKAnalyzer();

			//2.1 把查询字符串转为Query对象(只在title中查询)
			/*QueryParser queryParser = new QueryParser("title", analyzer);
			Query query = queryParser.parse(searchString);*/


			//Occur.MUST表示对应字段必须有查询值， Occur.MUST_NOT 表示对应字段必须没有查询值
			Occur[] occ = {Occur.MUST, Occur.MUST};
			Query query = MultiFieldQueryParser.parse(searchStrings, searchFields, occ, analyzer);


			//2.2 执行搜索得到结果
			IndexReader indexReader = DirectoryReader.open(directory);
			IndexSearcher indexSearcher = new IndexSearcher(indexReader);
			//返回查询出来的前n条结果
			TopDocs topDocs = indexSearcher.search(query, 100);

			//总结果数量
			long count = topDocs.totalHits;
			//返回前N条结果信息
			ScoreDoc[] scoreDocs = topDocs.scoreDocs;
			//2.3 处理结果
			for (int i = 0; i < scoreDocs.length; i++) {
				ScoreDoc scoreDoc = scoreDocs[i];
				int docId = scoreDoc.doc;
				LOGGER.info("得分是："+scoreDoc.score+"，内部编号是："+docId);

				//根据内部编号取出真正的Document数据
				Document doc = indexSearcher.doc(docId);

				//将document转化为Article
				SearchModel model = new SearchModel(doc.get("id"), doc.get("title"), doc.get("content"));
				models.add(model);
			}

			//3  控制台显示结果
			LOGGER.info("总结果数："+count);
			indexSearcher.getIndexReader().close();
		} catch (NumberFormatException e) {
			LOGGER.error(e.getMessage(), e);
		} catch (IOException e) {
			LOGGER.error(e.getMessage(), e);
		} catch (ParseException e) {
			LOGGER.error(e.getMessage(), e);
		}
		return models;
	}
}
